home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 1 (Walnut Creek)
/
Aminet - June 1993 [Walnut Creek].iso
/
usenet
/
sources
/
volume90
/
devices
/
msh_1_5
/
part06
< prev
Wrap
Internet Message Format
|
1990-02-21
|
52KB
Path: xanth!cs.odu.edu!Amiga-Request
From: Amiga-Request@cs.odu.edu (Amiga Sources/Binaries Moderator)
Newsgroups: comp.sources.amiga
Subject: v90i084: MSH 1.5 - Messydos File System Handler , Part06/06
Message-ID: <11503@xanth.cs.odu.edu>
Date: 21 Feb 90 02:03:02 GMT
Sender: tadguy@cs.odu.edu
Reply-To: Olaf 'Rhialto' Seibert <U211344%HNYKUN11.BITNET@CUNYVM.CUNY.EDU>
Lines: 1937
Approved: tadguy@cs.odu.edu (Tad Guy)
X-Mail-Submissions-To: Amiga@cs.odu.edu
X-Post-Discussions-To: comp.sys.amiga
Submitted-by: Olaf 'Rhialto' Seibert <U211344%HNYKUN11.BITNET@CUNYVM.CUNY.EDU>
Posting-number: Volume 90, Issue 084
Archive-name: devices/msh-1.5/part06
#!/bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of archive 6 (of 6)."
# Contents: src/devio.c
# Wrapped by tadguy@xanth on Tue Feb 20 20:57:14 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'src/devio.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'src/devio.c'\"
else
echo shar: Extracting \"'src/devio.c'\" \(47667 characters\)
sed "s/^X//" >'src/devio.c' <<'END_OF_FILE'
X/*-
X * $Id: devio.c,v 1.4 90/02/10 21:42:17 Rhialto Exp $
X * $Log: devio.c,v $
X * Revision 1.4 90/02/10 21:42:17 Rhialto
X * Small changes
X *
X * Revision 1.3 90/01/27 20:36:04 Rhialto
X * Variable #sectors/track!
X *
X * Revision 1.2 90/01/23 00:41:39 Rhialto
X * Remove C version of DecodeTrack.
X *
X * Revision 1.1 89/12/17 20:04:11 Rhialto
X *
X * DEVIO.C
X *
X * The messydisk.device code that does the real work.
X *
X * This code is (C) Copyright 1989 by Olaf Seibert. All rights reserved. May
X * not be used or copied without a licence.
X-*/
X
X#include "dev.h"
X#include "device.h"
X
X/*#undef DEBUG /**/
X#ifdef DEBUG
X# define debug(x) dbprintf x
X#else
X# define debug(x)
X#endif
X
Xstruct DiskResource *DRResource;/* Argh! A global variable! */
Xvoid *CiaBResource; /* And yet another! */
X
Xvoid Internal_Update();
Xword DataCRC();
Xword CalculateGapLength();
X
X/*-
X * The high clock bit in this table is still 0, but it could become
X * a 1 if the two adjecent data bits are both 0.
X * In fact, that is the principle of MFM clock bits: make sure that no
X * two 1 bits are adjecent, but not too many (more than 3) 0 bits either.
X * So, 0 c 0 -> 0 1 0 (where c is a clock bit to be determined)
X * 0 c 1 -> 0 0 1
X * 1 c 0 -> 1 0 0
X * 1 c 1 -> 1 0 1
X * The sync pattern, $4489, is %0100 0100 1000 1001
X * ~ ~ ~ ~ ~ ~ ~ ~ -> %1010 0001 -> $A1
X * also follows the rules, but won't be formed by encoding $A1...
X * Since the bytes are written high bit first, the unknown clock bit
X * (for encoded nybbles 0-7, high bit 0) will become a 1 if the preceding
X * byte was even (with low bit 0).
X * So, the clock bit is the NOR of the two data bits.
X-*/
X
Xbyte MfmEncode[16] = {
X 0x2a, 0x29, 0x24, 0x25, 0x12, 0x11, 0x14, 0x15,
X 0x4a, 0x49, 0x44, 0x45, 0x52, 0x51, 0x54, 0x55
X};
X
X#define SYNC 0x4489
X#define TLEN 12500 /* In BYTES */
X#define RLEN (TLEN+1324) /* 1 sector extra */
X#define WLEN (TLEN+20) /* 20 bytes more than the theoretical track size */
X
X#define INDEXGAP 60 /* All these values are in WORDS */
X
X#define IDGAP2 12 /* Sector header: 22 words */
X#define IDSYNC 3
X#define IDMARK 1
X#define IDDATA 4
X#define IDCRC 2
X#define IDLEN (IDGAP2+IDSYNC+IDMARK+IDDATA+IDCRC)
X
X#define DATAGAP1 22 /* Sector itself: 552 words */
X#define DATAGAP2 12
X#define DATASYNC 3
X#define DATAMARK 1
X#define DATACRC 2
X#define DATAGAP3_9 78 /* for 9 or less sectors/track */
X#define DATAGAP3_10 40 /* for 10 sectors/track */
X#define DATALEN (DATAGAP1+DATAGAP2+DATASYNC+DATAMARK+MS_BPS+DATACRC)
X
X#define BLOCKLEN (IDLEN+DATALEN) /* Total: 574 words */
X
X#define TAILGAP 50
X
X/* INDENT OFF */
X#asm
X
X; Some hardware data:
X
XSYNC equ $4489
XTLEN equ 12500 ; 2 miscrosecs/bit, 200 ms/track -> 100000 bits
XWLEN equ TLEN+20
X
X;;;;
X;
X; The following lengths are all in unencoded bytes (or encoded words)
X
XINDEXGAP equ 60
X
XIDGAP2 equ 12
XIDSYNC equ 3
XIDMARK equ 1
XIDDATA equ 4
XIDCRC equ 2
X
XDATAGAP1 equ 22
XDATAGAP2 equ 12
XDATASYNC equ 3
XDATAMARK equ 1
XDATACRC equ 2
X
Xcustom equ $DFF000
X
XDsklen equ $24
XIntena equ $9a ; Interrupt enable register (write)
XIntreq equ $9c ; Interrupt request register (write)
X
X; Flags in DSKLEN:
X
Xdskdmaoff equ $4000
X
X; Flags in INTENA/INTREQ:
X
Xintf_setclr equ 1<<15
Xintf_dskblk equ 1<<1
X
X; CIA interrupt control register bits/flags:
X
Xciaicrf_flg equ 1<<4 ; flg interrupt (disk index)
X
X; some cia.resource library functions
X
X public _LVOSignal
X public _LVOAbleICR
X public _LVOSetICR
X
X_SafeEnableICR: move.l _CiaBResource,a6
X move.b 4+1(sp),d0
X jsr _LVOSetICR(a6) ; clear pending interrupt
X move.b 4+1(sp),d0
X or.b #$80,d0 ; then enable it
X jsr _LVOAbleICR(a6)
X rts
X;;;;
X;
X; Disk index interrupt code.
X; is_Data (A1) is the value to stuff into the DSKLEN register.
X; A0 points to the custom chips already.
X; It then enables the disk block interrupt and disables the
X; index interrupt.
X
X_IndexIntCode:
X; movem.l A2-A4/D2-D7,-(sp)
X move.w #dskdmaoff,Dsklen(a0)
X move.w a1,Dsklen(a0)
X move.w a1,Dsklen(a0) ; this enables the DMA
X move.w #intf_setclr|intf_dskblk,Intena(a0)
X move.l _CiaBResource,a6
X move.b #ciaicrf_flg,d0
X jsr _LVOAbleICR(a6) ; disable index interrupt
X; movem.l (sp)+,A2-A4/D2-D7
X rts
X;;;;
X;
X; Disk DMA finished interrupt code.
X; (a1) is the task to Signal, 4(a1) is the signal mask to use.
X; Disables the disk block finished interrupt.
X
X_DskBlkIntCode:
X move.w #dskdmaoff,Dsklen(a0) ; disable disk DMA
X move.w #intf_dskblk,Intena(a0) ; disable the interrupt
X move.w #intf_dskblk,Intreq(a0) ; clear 'diskblock finished' flag
X move.l 4(a1),d0 ; signal mask
X move.l (a1),a1 ; task to signal
X jsr _LVOSignal(a6)
X rts
X
X#endasm
X
X#define DSKDMAEN (1<<15)
X#define DSKWRITE (1<<14)
X
Xvoid IndexIntCode(), DskBlkIntCode();
X
X/* INDENT ON */
X
Xint
XHardwareIO(dev, unit, dskwrite)
XDEV *dev;
Xregister UNIT *unit;
Xint dskwrite;
X{
X struct {
X struct Task *task;
X ulong signal;
X } tasksig;
X
X debug(("Disk buffer is at %lx\n", dev->md_Rawbuffer));
X
X tasksig.task = FindTask(NULL);
X tasksig.signal = 1L << unit->mu_DmaSignal;
X
X unit->mu_DRUnit.dru_Index.is_Data = (APTR) ((WLEN >> 1)|DSKDMAEN| dskwrite);
X unit->mu_DRUnit.dru_DiscBlock.is_Data = (APTR) &tasksig;
X
X /* Clear signal bit */
X SetSignal(0L, tasksig.signal);
X
X /* Allocate drive and install index and block interrupts */
X GetDrive(&unit->mu_DRUnit);
X
X /* Select correct drive and side */
X ciab.ciaprb = 0xff & ~CIAF_DSKMOTOR; /* See hardware manual p229 */
X ciab.ciaprb = 0xff & ~CIAF_DSKMOTOR
X & ~(CIAF_DSKSEL0 << unit->mu_UnitNr)
X & ~(unit->mu_CurrentSide << CIAB_DSKSIDE);
X
X /* Set up disk parameters */
X
X/*
X * This is the adkcon setup: MFM mode, wordsync, no MSBsync, fast mode.
X * The precomp is 0 nanoseconds for the outer half of the disk, 120 for
X * the rest.
X */
X {
X register word adk;
X
X custom.adkcon = ADKF_PRECOMP1|ADKF_PRECOMP0|ADKF_MSBSYNC;
X
X adk = ADKF_SETCLR|ADKF_MFMPREC|ADKF_FAST|ADKF_WORDSYNC;
X
X /* Are we on the inner half ? */
X if (unit->mu_CurrentTrack > unit->mu_NumCyls >> 1) {
X adk |= ADKF_PRECOMP0;
X }
X custom.adkcon = adk;
X }
X
X /* Set up disk buffer address */
X custom.dskpt = (APTR) dev->md_Rawbuffer;
X
X /* Enable disk DMA */
X custom.dmacon = DMAF_SETCLR | DMAF_MASTER | DMAF_DISK;
X
X if (dskwrite) {
X /* Enable disk index interrupt to start the whole thing. */
X SafeEnableICR((int) CIAICRF_FLG);
X } else {
X /* Set the sync word */
X custom.dsksync = SYNC;
X
X /* Do the same as the disk index interrupt would */
X custom.dsklen = DSKDMAOFF;
X custom.dsklen = (RLEN >> 1) | DSKDMAEN;
X custom.dsklen = (RLEN >> 1) | DSKDMAEN;
X
X custom.intena = INTF_SETCLR | INTF_DSKBLK;
X }
X
X Wait(tasksig.signal);
X
X FreeDrive();
X}
X
X#if 0
X#define ID_ADDRESS_MARK 0xFE
X#define MFM_ID 0x5554
X#define DATA_ADDRESS_MARK 0xFB
X#define MFM_DATA 0x5545
X
X/* INDENT OFF
Xbyte
XDecodeByte(mfmdecode, mfm)
Xbyte *mfmdecode;
Xword mfm;
X{
X return mfmdecode[(byte)mfm & 0x7F] |
X mfmdecode[(byte)(mfm >> 8) & 0x7F] << 4;
X} */
X
X#asm
Xmfmdecode set 4
Xmfm set 8
X
X_DecodeByte:
X move.l mfmdecode(sp),a0
X move.b mfm(sp),d1 ; high nybble
X and.w #$7f,d1 ; strip clock bit (and garbage)
X move.b (a0,d1.w),d0 ; decode 4 data bits
X lsl.b #4,d0 ; make room for the rest
X
X move.b mfm+1(sp),d1 ; low nybble
X and.b #$7f,d1 ; strip clock bit again
X or.b (a0,d1.w),d0 ; insert 4 decoded bits
X
X rts
X
X#endasm
X
X/* INDENT ON */
Xbyte DecodeByte();
X
Xint
XDecodeTrack(dev, unit)
XDEV *dev;
XUNIT *unit;
X{
X register word *rawbuf = (word *)dev->md_Rawbuffer; /* a2 */
X byte *rawend = (byte *)rawbuf + RLEN - (MS_BPS+2)*sizeof(word);
X byte *trackbuf = unit->mu_TrackBuffer;
X register byte *decode = dev->md_MfmDecode; /* a3 */
X word *oldcrc = unit->mu_CrcBuffer;
X register byte *secptr; /* a4 */
X long sector;
X word numsecs;
X register long numbytes; /* d3 */
X word maxsec;
X
X#define Len ((byte *)rawbuf - dev->md_Rawbuffer)
X maxsec = 0;
X
X for (numsecs = 0; numsecs < MS_SPT_MAX; numsecs++) {
X /*
X * First try to find a sector id.
X */
Xfind_id:
X while (*rawbuf != SYNC) {
X if (++rawbuf >= rawend) {
X debug(("id start, EOT %4x\n", Len));
X goto end;
X }
X }
X while (*rawbuf == SYNC) {
X rawbuf++;
X }
X if (*rawbuf++ != MFM_ID) {
X debug(("No ID (%4x), %4x\n", rawbuf[-1], Len));
X goto find_id;
X }
X
X sector = DecodeByte(decode, *rawbuf++);
X if (sector != unit->mu_CurrentTrack) {
X debug(("Track error?? %d\n", (int)sector));
X goto find_id;
X }
X sector = DecodeByte(decode, *rawbuf++);
X if (sector != unit->mu_CurrentSide) {
X debug(("Side error?? %d\n", (int)sector));
X goto find_id;
X }
X if (rawbuf >= rawend) {
X debug(("id end, EOT %4x\n", Len));
X goto end;
X }
X sector = DecodeByte(decode, *rawbuf++);
X debug(("#%2d %4x, ", (int)sector, Len-0xC));
X if (sector > MS_SPT_MAX) {
X debug(("Bogus sector number) "));
X goto find_id;
X }
X if (sector > maxsec)
X maxsec = sector;
X sector--; /* Normalize sector number */
X
X /*
X * Then find the data block.
X */
Xfind_data:
X while (*rawbuf != SYNC) {
X if (++rawbuf >= rawend) {
X debug(("data start, EOT %4x\n", Len));
X return 0; /* TDERR_TooFewSecs; */
X }
X }
X while (*rawbuf == SYNC) {
X rawbuf++;
X }
X if (*rawbuf++ != MFM_DATA) {
X debug(("No Data (%4x), %4x\n", rawbuf[-1], Len));
X goto find_id;
X }
X debug(("%4x, ", Len-8));
X
X if (rawbuf >= rawend) {
X debug(("short data, EOT %4x\n", Len));
X goto end;
X }
X secptr = trackbuf + MS_BPS * sector;
X for (numbytes = 0; numbytes < MS_BPS; numbytes++) {
X *secptr++ = DecodeByte(decode, *rawbuf++);
X }
X debug(("%4x\n", Len));
X oldcrc[sector] = DecodeByte(decode, *rawbuf++) << 8;
X oldcrc[sector] |= DecodeByte(decode, *rawbuf++);
X unit->mu_SectorStatus[sector] = unit->mu_InitSectorStatus;
X }
X
Xend:
X if (numsecs == 0)
X return TDERR_TooFewSecs;
X
X#ifndef READONLY
X /*
X * If we read the very first track, we adjust our notion about the
X * number of sectors on each track. This is the only track we can
X * accurately find if this number is unknown. Let's hope that the first
X * user of this disk starts reading it here.
X */
X if (unit->mu_CurrentTrack == 0 && unit->mu_CurrentSide == 0) {
X unit->mu_SectorsPerTrack = maxsec;
X }
X unit->mu_CurrentSectors = maxsec;
X debug(("%d sectors\n", unit->mu_SectorsPerTrack));
X#endif
X
X return 0;
X
X#undef Len
X}
X#else /* Use assembly */
X
Xint
XDecodeTrack(dev, unit)
XDEV *dev;
XUNIT *unit;
X{
X register word *rawbuf = (word *)dev->md_Rawbuffer; /* a2 */
X byte *rawend = (byte *)rawbuf + RLEN - (MS_BPS+2)*sizeof(word);
X byte *trackbuf = unit->mu_TrackBuffer;
X register byte *decode = dev->md_MfmDecode; /* a3 */
X word *oldcrc = unit->mu_CrcBuffer;
X register byte *secptr; /* a4 */
X long sector;
X word numsecs;
X register long numbytes; /* d3 */
X word maxsec;
X
X#asm
X
XMFM_ID equ $5554
XMFM_DATA equ $5545
X
Xrawbuf equr a2
Xdecode equr a3
Xsecptr equr a4
Xnumbytes equr d3
X
Xrawend set -4
Xtrackbuf set -8
Xoldcrc set -12
Xsector set -16
Xnumsecs set -18
Xmaxsec set -20
X
X move.w #0,numsecs(a5) ; no sectors found yet
X move.w #0,maxsec(a5) ; and no highest sector number
X
X;;;; First we will try to find a sector id.
Xfind_id:
X cmp #SYNC,(rawbuf)+
X beq.s fid_gotsync
X cmpa.l rawend(a5),rawbuf
X blt find_id
X bra return ; We ran off the end of the buffer.
X
Xfid_gotsync: ; Skip the other syncs.
X cmp.w #SYNC,(rawbuf)
X bne fid_endsync
X lea 2(rawbuf),rawbuf
X bra fid_gotsync
X
Xfid_endsync:
X cmp.w #MFM_ID,(rawbuf)+
X bne find_id
X
X bsr DecodeByte ; track #
X bsr DecodeByte ; side #
X moveq.l #0,d0 ; clear high part
X bsr DecodeByte ; sector #
X cmp.w #MS_SPT_MAX,d0 ; sector number too large?
X bgt find_id
X cmp.w maxsec(a5),d0 ; what is the highest sector number?
X ble nomax
X move.w d0,maxsec(a5) ; record the highest sector number
Xnomax:
X subq.w #1,d0 ; normalize sector number
X move.l d0,sector(a5)
X
Xfind_data: ; Then find the data block.
X cmp #SYNC,(rawbuf)+
X beq.s fda_gotsync
X cmpa.l rawend(a5),rawbuf
X blt find_data
X bra return ; we ran off the end of the buffer.
X
Xfda_gotsync: ; skip the other syncs.
X cmp.w #SYNC,(rawbuf)
X bne fda_endsync
X lea 2(rawbuf),rawbuf
X bra fda_gotsync
X
Xfda_endsync:
X cmp.w #MFM_DATA,(rawbuf)+ ; do we really have a data block?
X bne find_id
X
X cmpa.l rawend(a5),rawbuf ; will we still be inside the mfm data?
X bge return
X
X move.l sector(a5),d0 ; calculate the location to
X moveq.l #LOG2_MS_BPS,d1 ; store this sector.
X asl.l d1,d0
X move.l trackbuf(a5),secptr
X add.l d0,secptr
X
X move.w #MS_BPS-1,numbytes
Xdata_copy:
X bsr DecodeByte
X move.b d0,(secptr)+
X dbra numbytes,data_copy
X
X move.l sector(a5),d3 ; get pointer to crc location
X add.l d3,d3 ; 2 bytes of crc per sector
X move.l oldcrc(a5),a0
X add.l d3,a0
X
X bsr DecodeByte ; get high byte
X move.b d0,(a0)+
X bsr DecodeByte ; and low byte of crc
X move.b d0,(a0)+
X
X#endasm
X unit->mu_SectorStatus[sector] = unit->mu_InitSectorStatus;
X#asm
X addq.w #1,numsecs(a5)
X cmp.w #MS_SPT_MAX,numsecs(a5)
X blt find_id
Xreturn:
X#endasm
X
X if (numsecs == 0)
X return TDERR_TooFewSecs;
X
X#ifndef READONLY
X /*
X * If we read the very first track, we adjust our notion about the
X * number of sectors on each track. This is the only track we can
X * accurately find if this number is unknown. Let's hope that the first
X * user of this disk starts reading it here.
X */
X if (unit->mu_CurrentTrack == 0 && unit->mu_CurrentSide == 0) {
X unit->mu_SectorsPerTrack = maxsec;
X }
X unit->mu_CurrentSectors = maxsec;
X debug(("%d sectors\n", unit->mu_SectorsPerTrack));
X#endif
X
X return 0;
X
X}
X
X#asm
X;;;;
X;
X; Decode a single MFM word to a byte.
X; Auto-increments the rawbuffer pointer.
X
XDecodeByte:
X move.b (rawbuf)+,d1 ; high nybble
X and.w #$7f,d1 ; strip clock bit (and garbage)
X move.b (decode,d1.w),d0; decode 4 data bits
X lsl.b #4,d0 ; make room for the rest
X
X move.b (rawbuf)+,d1 ; low nybble
X and.b #$7f,d1 ; strip clock bit again
X or.b (decode,d1.w),d0; insert 4 decoded bits
X
X rts
X
X#endasm
X#endif /* using assembly */
X
X/*
X * Initialize the ibm mfm decoding table
X */
X
Xvoid
XInitDecoding(decode)
Xregister byte *decode;
X{
X register int i;
X
X i = 0;
X do {
X decode[i] = 0xff;
X } while (++i < 128);
X
X i = 0;
X do {
X decode[MfmEncode[i]] = i;
X } while (++i < 0x10);
X}
X
X#ifdef notdef
Xlong
XMyDoIO(req)
Xregister struct IORequest *req;
X{
X req->io_Flags |= IOF_QUICK;
X BeginIO(req);
X return WaitIO(req);
X}
X#endif
X
X/*
X * Switch the drive motor on. Return previous state. Don't use this when
X * you have allocated the disk via GetDrive().
X */
X
Xint
XTDMotorOn(tdreq)
Xregister struct IOExtTD *tdreq;
X{
X debug(("TDMotorOn "));
X tdreq->iotd_Req.io_Command = TD_MOTOR;
X tdreq->iotd_Req.io_Length = 1;
X DoIO(tdreq);
X debug(("was %ld\n", tdreq->iotd_Req.io_Actual));
X
X return tdreq->iotd_Req.io_Actual;
X}
X
X/*
X * Get the number of cylinders the drive is capable of using.
X */
X
Xint
XTDGetNumCyls(tdreq)
Xregister struct IOExtTD *tdreq;
X{
X tdreq->iotd_Req.io_Command = TD_GETNUMTRACKS;
X DoIO(tdreq);
X
X return tdreq->iotd_Req.io_Actual / NUMHEADS;
X}
X
X/*
X * Seek the drive to the indicated cylinder. Use the trackdisk.device for
X * ease. Don't use this when you have allocated the disk via GetDrive().
X */
X
Xint
XTDSeek(unit, ioreq, cylinder)
XUNIT *unit;
Xstruct IOStdReq *ioreq;
Xint cylinder;
X{
X register struct IOExtTD *tdreq = unit->mu_DiskIOReq;
X
X debug(("TDSeek %d\n", cylinder));
X
X tdreq->iotd_Req.io_Command = TD_SEEK;
X tdreq->iotd_Req.io_Offset = cylinder * (TD_SECTOR * NUMSECS * NUMHEADS);
X if ((ioreq->io_Flags & IOMDF_40TRACKS) && (unit->mu_NumCyls == 80))
X tdreq->iotd_Req.io_Offset *= 2;
X DoIO(tdreq);
X
X return tdreq->iotd_Req.io_Error;
X}
X
Xvoid *
XGetDrive(drunit)
Xregister struct DiskResourceUnit *drunit;
X{
X register void *LastDriver;
X
X debug(("GetDrive: "));
X for (;;) {
X drunit->dru_Message.mn_Node.ln_Type = NT_MESSAGE;
X LastDriver = GetUnit(drunit);
X
X debug(("LastDriver %08lx\n", LastDriver));
X if (LastDriver) {
X return LastDriver;
X } else {
X while (drunit->dru_Message.mn_Node.ln_Type != NT_REPLYMSG)
X Wait(1L << drunit->dru_Message.mn_ReplyPort->mp_SigBit);
X Remove(drunit);
X debug(("GetDrive: Retry\n"));
X }
X }
X}
X
Xvoid
XFreeDrive()
X{
X GiveUnit();
X}
X
Xint
XGetTrack(ioreq, side, track)
Xstruct IOStdReq *ioreq;
Xint side;
Xint track;
X{
X register int i;
X DEV *dev;
X register UNIT *unit;
X
X debug(("GetTrack %d %d\n", track, side));
X dev = (DEV *) ioreq->io_Device;
X unit = (UNIT *) ioreq->io_Unit;
X
X if (track != unit->mu_CurrentTrack || side != unit->mu_CurrentSide) {
X#ifndef READONLY
X Internal_Update(ioreq, unit);
X#endif
X for (i = MS_SPT_MAX-1; i >= 0; i--) {
X unit->mu_SectorStatus[i] = TDERR_NoSecHdr;
X }
X
X TDMotorOn(unit->mu_DiskIOReq);
X if (TDSeek(unit, ioreq, track)) {
X debug(("Seek error\n"));
X return ioreq->io_Error = IOERR_BADLENGTH;
X }
X unit->mu_CurrentTrack = track;
X unit->mu_CurrentSide = side;
X ObtainSemaphore(&dev->md_HardwareUse);
X HardwareIO(dev, unit, 0);
X i = DecodeTrack(dev, unit);
X ReleaseSemaphore(&dev->md_HardwareUse);
X debug(("DecodeTrack returns %d\n", i));
X
X if (i != 0) {
X unit->mu_CurrentTrack = -1;
X return i;
X }
X }
X
X return 0;
X}
X
X/*
X * Test if it is changed
X */
X
Xint
XCheckChanged(ioreq, unit)
Xstruct IOExtTD *ioreq;
Xregister UNIT *unit;
X{
X register struct IOExtTD *tdreq;
X
X if ((ioreq->iotd_Req.io_Command & TDF_EXTCOM) &&
X ioreq->iotd_Count < unit->mu_ChangeNum) {
Xdiskchanged:
X ioreq->iotd_Req.io_Error = TDERR_DiskChanged;
Xerror:
X return 1;
X }
X return 0;
X}
X
X/*
X * Test if we can read or write the disk. Is it inserted and writable?
X */
X
Xint
XCheckRequest(ioreq, unit)
Xstruct IOExtTD *ioreq;
Xregister UNIT *unit;
X{
X register struct IOExtTD *tdreq;
X
X if ((ioreq->iotd_Req.io_Command & TDF_EXTCOM) &&
X ioreq->iotd_Count < unit->mu_ChangeNum) {
Xdiskchanged:
X ioreq->iotd_Req.io_Error = TDERR_DiskChanged;
Xerror:
X return 1;
X }
X /*
X * if (ioreq->iotd_Req.io_Offset + ioreq->iotd_Req.io_Length >
X * (unit->mu_NumCyls * MS_NSIDES * MS_SPT * MS_BPS)) {
X * ioreq->iotd_Req.io_Error = IOERR_BADLENGTH; goto error; }
X */
X
X tdreq = unit->mu_DiskIOReq;
X
X if (unit->mu_DiskState == STATEF_UNKNOWN) {
X tdreq->iotd_Req.io_Command = TD_PROTSTATUS;
X DoIO(tdreq);
X if (tdreq->iotd_Req.io_Error == 0) {
X if (tdreq->iotd_Req.io_Actual == 0) {
X unit->mu_DiskState = STATEF_PRESENT | STATEF_WRITABLE;
X } else
X unit->mu_DiskState = STATEF_PRESENT;
X } else
X unit->mu_DiskState = 0;
X }
X if (!(unit->mu_DiskState & STATEF_PRESENT))
X goto diskchanged;
X
X /*
X * Check _WRITE, _UPDATE, _FORMAT
X */
X if (STRIP(ioreq->iotd_Req.io_Command) != CMD_READ) {
X if (!(unit->mu_DiskState & STATEF_WRITABLE)) {
X ioreq->iotd_Req.io_Error = TDERR_WriteProt;
X goto error;
X }
X }
X return 0;
X}
X
X
X/*
X * Read zero or more sectors from the disk and copy them into the user's
X * buffer.
X */
X
Xvoid
XCMD_Read(ioreq, unit)
Xregister struct IOExtTD *ioreq;
Xregister UNIT *unit;
X{
X int side;
X int cylinder;
X int sector;
X byte *userbuf;
X long length;
X long offset;
X byte *diskbuf;
X int retrycount;
X
X debug(("CMD_Read "));
X userbuf = (byte *) ioreq->iotd_Req.io_Data;
X length = ioreq->iotd_Req.io_Length / MS_BPS; /* Sector count */
X offset = ioreq->iotd_Req.io_Offset / MS_BPS; /* Sector number */
X debug(("userbuf %08lx off %ld len %ld ", userbuf, offset, length));
X
X cylinder = offset / unit->mu_SectorsPerTrack;
X side = cylinder % MS_NSIDES;
X cylinder /= MS_NSIDES;
X sector = offset % unit->mu_SectorsPerTrack; /* 0..8 or 9 */
X debug(("Tr=%d Si=%d Se=%d\n", cylinder, side, sector));
X
X ioreq->iotd_Req.io_Actual = 0;
X
X if (length <= 0 || CheckRequest(ioreq, unit))
X goto end;
X
X retrycount = 0;
X diskbuf = unit->mu_TrackBuffer + MS_BPS * sector;
Xgettrack:
X GetTrack(ioreq, side, cylinder);
X
X for (;;) {
X /*
X * Have we ever checked this CRC?
X */
X if (unit->mu_SectorStatus[sector] == CRC_UNCHECKED) {
X /*
X * Do it now. If it mismatches, remember that for later.
X */
X if (unit->mu_CrcBuffer[sector] != DataCRC(diskbuf)) {
X debug(("%d: %04x, now %04x\n", sector, unit->mu_CrcBuffer[sector], DataCRC(diskbuf)));
X unit->mu_SectorStatus[sector] = TDERR_BadSecSum;
X } else
X unit->mu_SectorStatus[sector] = TDERR_NoError;
X }
X if (unit->mu_SectorStatus[sector] > TDERR_NoError) {
X if (++retrycount < 3) {
X unit->mu_CurrentTrack = -1;
X goto gettrack;
X }
X ioreq->iotd_Req.io_Error = unit->mu_SectorStatus[sector];
X goto end; /* Don't use this sector anymore... */
X }
X retrycount = 0;
X CopyMem(diskbuf, userbuf, (long) MS_BPS);
X ioreq->iotd_Req.io_Actual += MS_BPS;
X if (--length <= 0)
X break;
X userbuf += MS_BPS;
X diskbuf += MS_BPS;
X if (++sector >= unit->mu_SectorsPerTrack) {
X sector = 0;
X diskbuf = unit->mu_TrackBuffer;
X if (++side >= MS_NSIDES) {
X side = 0;
X if (++cylinder >= unit->mu_NumCyls) {
X /* ioreq->iotd_Req.io_Error = IOERR_BADLENGTH; */
X goto end;
X }
X }
X GetTrack(ioreq, side, cylinder);
X }
X }
X
Xend:
X TermIO(ioreq);
X}
X
X#ifdef READONLY
X
Xvoid
XCMD_Write(ioreq, unit)
Xregister struct IOExtTD *ioreq;
XUNIT *unit;
X{
X ioreq->iotd_Req.io_Error = TDERR_NotSpecified;
X TermIO(ioreq);
X}
X
Xvoid
XTD_Format(ioreq, unit)
Xregister struct IOExtTD *ioreq;
XUNIT *unit;
X{
X ioreq->iotd_Req.io_Error = TDERR_NotSpecified;
X TermIO(ioreq);
X}
X
X#endif
X
Xvoid
XCMD_Reset(ioreq, unit)
Xstruct IOExtTD *ioreq;
XUNIT *unit;
X{
X unit->mu_CurrentSide = -1;
X unit->mu_TrackChanged = 0;
X TermIO(ioreq);
X}
X
Xvoid
XCMD_Update(ioreq, unit)
Xstruct IOExtTD *ioreq;
Xregister UNIT *unit;
X{
X#ifndef READONLY
X if (unit->mu_TrackChanged && !CheckRequest(ioreq, unit))
X Internal_Update(ioreq, unit);
X#endif
X TermIO(ioreq);
X}
X
Xvoid
XCMD_Clear(ioreq, unit)
Xstruct IOExtTD *ioreq;
XUNIT *unit;
X{
X if (!CheckChanged(ioreq, unit)) {
X unit->mu_CurrentSide = -1;
X unit->mu_TrackChanged = 0;
X }
X TermIO(ioreq);
X}
X
Xvoid
XTD_Seek(ioreq, unit)
Xstruct IOExtTD *ioreq;
XUNIT *unit;
X{
X if (!CheckChanged(ioreq, unit)) {
X word cylinder;
X
X cylinder = (ioreq->iotd_Req.io_Offset / unit->mu_SectorsPerTrack) /
X (MS_BPS * MS_NSIDES);
X TDSeek(unit, ioreq, cylinder);
X }
X TermIO(ioreq);
X}
X
X/*
X * Ask the trackdisk.device for the answer, but keep a local copy.
X */
X
Xvoid
XTD_Changenum(ioreq, unit)
Xstruct IOExtTD *ioreq;
XUNIT *unit;
X{
X register struct IOStdReq *req;
X
X req = &unit->mu_DiskIOReq->iotd_Req;
X req->io_Command = TD_CHANGENUM;
X DoIO(req);
X
X unit->mu_ChangeNum = req->io_Actual;
X ioreq->iotd_Req.io_Actual = req->io_Actual;
X TermIO(ioreq);
X}
X
Xint
XDevInit(dev)
Xregister DEV *dev;
X{
X if (!(DRResource = OpenResource(DISKNAME)))
X goto abort;
X
X if (!(CiaBResource = OpenResource(CIABNAME)))
X goto abort;
X
X#ifndef READONLY
X if (!InitWrite(dev))
X goto abort;
X#endif
X
X InitDecoding(dev->md_MfmDecode);
X InitSemaphore(&dev->md_HardwareUse);
X return 1; /* Initializing succeeded */
X
Xabort:
X return DevCloseDown(dev);
X}
X
Xint
XDevCloseDown(dev)
XDEV *dev;
X{
X#ifndef READONLY
X FreeBuffer(dev);
X#endif
X return 0; /* Now unitialized */
X}
X
X#ifndef READONLY
X/*
X * Calculate the length between the sectors, given the length of the track
X * and the number of sectors that must fit on it.
X * The proper formula would be
X * (((TLEN/2) - INDEXGAP - TAILGAP) / unit->mu_SectorsPerTrack) - BLOCKLEN;
X */
X
Xword
XCalculateGapLength(sectors)
Xint sectors;
X{
X return (sectors == 10) ? DATAGAP3_10 : DATAGAP3_9;
X}
X#endif
X
XUNIT *
XUnitInit(dev, UnitNr)
XDEV *dev;
Xulong UnitNr;
X{
X register UNIT *unit;
X struct Task *task;
X struct IOStdReq *dcr;
X struct IOExtTD *tdreq;
X
X unit = AllocMem((long) sizeof (UNIT), MEMF_PUBLIC | MEMF_CLEAR);
X if (unit == NULL)
X return NULL;
X
X if (!(tdreq = CreateExtIO(&unit->mu_DiskReplyPort, (long) sizeof (*tdreq)))) {
X goto abort;
X }
X unit->mu_DiskIOReq = tdreq;
X if (OpenDevice(TD_NAME, UnitNr, tdreq, TDF_ALLOW_NON_3_5)) {
X tdreq->iotd_Req.io_Device = NULL;
X goto abort;
X }
X dcr = (void *) CreateExtIO(&unit->mu_DiskReplyPort, (long) sizeof (*dcr));
X if (dcr) {
X unit->mu_DiskChangeReq = dcr;
X unit->mu_DiskChangeInt.is_Node.ln_Pri = 32;
X unit->mu_DiskChangeInt.is_Data = (APTR) unit;
X unit->mu_DiskChangeInt.is_Code = DiskChangeHandler;
X /* Clone IO request part */
X dcr->io_Device = tdreq->iotd_Req.io_Device;
X dcr->io_Unit = tdreq->iotd_Req.io_Unit;
X dcr->io_Command = TD_ADDCHANGEINT;
X dcr->io_Data = (void *) &unit->mu_DiskChangeInt;
X SendIO(dcr);
X }
X NewList(&unit->mu_ChangeIntList);
X
X unit->mu_NumCyls = TDGetNumCyls(tdreq);
X unit->mu_UnitNr = UnitNr;
X unit->mu_DiskState = STATEF_UNKNOWN;
X unit->mu_TrackChanged = 0;
X unit->mu_CurrentSide = -1;
X unit->mu_InitSectorStatus = CRC_UNCHECKED;
X unit->mu_SectorsPerTrack = MS_SPT;
X
X unit->mu_DRUnit.dru_Message.mn_ReplyPort = &unit->mu_DiskReplyPort;
X unit->mu_DRUnit.dru_Index.is_Node.ln_Pri = 32; /* high pri for index int */
X unit->mu_DRUnit.dru_Index.is_Code = IndexIntCode;
X unit->mu_DRUnit.dru_DiscBlock.is_Code = DskBlkIntCode;
X
X /*
X * Now create the Unit task. Remember that it won't start running
X * since we are Forbid()den. But just to be sure, we Forbid() again.
X */
X Forbid();
X task = CreateTask(DevName, TASKPRI, UnitTask, TASKSTACK);
X task->tc_UserData = (APTR) unit;
X
X unit->mu_Port.mp_Flags = PA_IGNORE;
X unit->mu_Port.mp_SigTask = task;
X NewList(&unit->mu_Port.mp_MsgList);
X
X unit->mu_DiskReplyPort.mp_Flags = PA_IGNORE;
X unit->mu_DiskReplyPort.mp_SigTask = task;
X NewList(&unit->mu_DiskReplyPort.mp_MsgList);
X Permit();
X
X return unit;
X
Xabort:
X UnitCloseDown(NULL, dev, unit);
X return NULL;
X}
X
Xint
XUnitCloseDown(ioreq, dev, unit)
Xstruct IOExtTD *ioreq;
XDEV *dev;
Xregister UNIT *unit;
X{
X#ifndef READONLY
X if (ioreq && unit->mu_TrackChanged)
X Internal_Update(ioreq, unit);
X#endif
X
X /*
X * Get rid of the Unit's task. We know this is safe because the unit
X * has an open count of zero, so it is 'guaranteed' not in use.
X */
X
X if (unit->mu_Port.mp_SigTask) {
X#ifdef DEBUG
X extern struct SignalSemaphore PortUse;
X
X /*
X * Make sure that the unit task does not get removed when it has
X * the semaphore.
X */
X ObtainSemaphore(&PortUse);
X#endif
X RemTask(unit->mu_Port.mp_SigTask);
X#ifdef DEBUG
X ReleaseSemaphore(&PortUse);
X#endif
X }
X if (unit->mu_DiskChangeReq) {
X#if 0 /* V1.2 and V1.3 have a broken
X * TD_REMCHANGEINT */
X register struct IOExtTD *req = unit->mu_DiskIOReq;
X
X req->iotd_Req.io_Command = TD_REMCHANGEINT;
X req->iotd_Req.io_Data = (void *) unit->mu_DiskChangeReq;
X DoIO(req);
X WaitIO(unit->mu_DiskChangeReq);
X#else
X Disable();
X Remove(unit->mu_DiskChangeReq);
X Enable();
X#endif
X DeleteExtIO(unit->mu_DiskChangeReq);
X unit->mu_DiskChangeReq = NULL;
X }
X if (unit->mu_DiskIOReq) {
X if (unit->mu_DiskIOReq->iotd_Req.io_Device)
X CloseDevice(unit->mu_DiskIOReq);
X DeleteExtIO(unit->mu_DiskIOReq);
X unit->mu_DiskIOReq = NULL;
X }
X FreeMem(unit, (long) sizeof (UNIT));
X
X return 0; /* Now unitialized */
X}
X
X/*
X * Create missing bindings
X */
X/* INDENT OFF */
X
X#asm
X
Xlib_vectsize equ 6
Xlib_base equ -lib_vectsize
X
X_RVOAllocUnit equ lib_base-(0*lib_vectsize)
X_RVOFreeUnit equ lib_base-(1*lib_vectsize)
X_RVOGetUnit equ lib_base-(2*lib_vectsize)
X_RVOGiveUnit equ lib_base-(3*lib_vectsize)
X_RVOGetUnitID equ lib_base-(4*lib_vectsize)
X
X;_AllocUnit:
X; move.l _DRResource,a6
X; move.l 4(sp),d0
X; jmp _RVOAllocUnit(a6)
X;_FreeUnit:
X; move.l _DRResource,a6
X; move.l 4(sp),d0
X; jmp _RVOFreeUnit(a6)
X_GetUnit:
X move.l _DRResource,a6
X move.l 4(sp),a1
X jmp _RVOGetUnit(a6)
X;_GetUnitID:
X; move.l _DRResource,a6
X; move.l 4(sp),d0
X; jmp _RVOGetUnitID(a6)
X_GiveUnit:
X move.l _DRResource,a6
X jmp _RVOGiveUnit(a6)
X
X#endasm
X/* INDENT ON */
X
X/*
X * We handle disk change interrupts internally, since the io request is
X * held by the device. Since SoftInts caused by the trackdisk.device are
X * broadcast to our clients, our own softint must have the highest
X * priority possible.
X *
X * TD_Addchangeint is an IMMEDIATE command, so no exclusive use of the list
X * is acquired (nor released). The list is accessed by (software)
X * interrupt code.
X */
X
Xvoid
XTD_Addchangeint(ioreq)
Xregister struct IOStdReq *ioreq;
X{
X register UNIT *unit;
X
X unit = (UNIT *) ioreq->io_Unit;
X Disable();
X AddTail(&unit->mu_ChangeIntList, ioreq);
X Enable();
X ioreq->io_Flags &= ~IOF_QUICK; /* So we call ReplyMsg instead of
X * TermIO */
X /* Note no TermIO */
X}
X
Xvoid
XTD_Remchangeint(ioreq)
Xregister struct IOStdReq *ioreq;
X{
X register struct IOStdReq *intreq;
X
X intreq = (struct IOStdReq *) ioreq->io_Data;
X Disable();
X Remove(intreq);
X Enable();
X ReplyMsg(&intreq->io_Message); /* Quick bit always cleared */
X ioreq->io_Error = 0;
X TermIO(ioreq);
X}
X
Xvoid
XDiskChangeHandler()
X{
X auto UNIT *unit;
X register struct IOStdReq *ioreq;
X register struct IOStdReq *next;
X/* INDENT OFF */
X#asm
X movem.l d2-d7/a2-a4,-(sp)
X move.l a1,-4(a5) ;unit
X#endasm
X /* INDENT ON */
X unit->mu_DiskState = STATEF_UNKNOWN;
X unit->mu_ChangeNum++;
X unit->mu_SectorsPerTrack = MS_SPT;
X for (ioreq = (struct IOStdReq *) unit->mu_ChangeIntList.mlh_Head;
X next = (struct IOStdReq *) ioreq->io_Message.mn_Node.ln_Succ;
X ioreq = next) {
X Cause((struct Interrupt *) ioreq->io_Data);
X }
X/* INDENT OFF */
X#asm
X movem.l (sp)+,d2-d7/a2-a4
X#endasm
X /* INDENT ON */
X}
X
X#ifndef READONLY
X
X/*
X * Parts of the following code were written by Werner Guenther.
X * Used with permission.
X */
X
X/* mu_TrackChanged is a flag. When a sector has changed it changes to 1 */
X
X/*
X * InitWrite() has to be called once at startup. It allocates the space
X * for one raw track, and writes the low level stuff between sectors
X * (gaps, syncs etc.)
X */
X
Xint
XInitWrite(dev)
XDEV *dev;
X{
X if ((dev->md_Rawbuffer =
X AllocMem((long)RLEN+2, MEMF_CHIP | MEMF_PUBLIC)) == 0)
X return 0;
X
X return 1;
X}
X
X/*
X * FreeBuffer has to be called when msh: closes down, it just frees the
X * memory InitWrite has allocated
X */
X
Xvoid
XFreeBuffer(dev)
XDEV *dev;
X{
X if (dev->md_Rawbuffer) { /* OIS */
X FreeMem(dev->md_Rawbuffer, (long) RLEN + 2);
X }
X}
X
X/*
X * This routine doesn't write to the disk, but updates the TrackBuffer to
X * respect the new sector. We have to be sure the TrackBuffer is filled
X * with the current Track. As GetSTS calls Internal_Update if the track
X * changes we don't have to bother about actually writing any data to the
X * disk. GetSTS has to be changed in the following way:
X *
X * if (track != mu_CurrentTrack || side != mu_CurrentSide) { Internal_Update(); for
X * (i = 0; i < MS_SPT; i++) ..... etc.
X */
X
Xvoid
XCMD_Write(ioreq, unit)
Xregister struct IOExtTD *ioreq;
XUNIT *unit;
X{
X int side;
X int cylinder;
X int sector;
X byte *userbuf;
X long length;
X long offset;
X word spt;
X
X debug(("CMD_Write "));
X userbuf = (byte *) ioreq->iotd_Req.io_Data;
X length = ioreq->iotd_Req.io_Length / MS_BPS; /* Sector count */
X offset = ioreq->iotd_Req.io_Offset / MS_BPS; /* Sector number */
X debug(("userbuf %08lx off %ld len %ld ", userbuf, offset, length));
X
X spt = unit->mu_SectorsPerTrack;
X cylinder = offset / spt;
X side = cylinder % MS_NSIDES;
X cylinder /= MS_NSIDES;
X sector = offset % spt;
X debug(("T=%d Si=%d Se=%d\n", cylinder, side, sector));
X
X ioreq->iotd_Req.io_Actual = 0;
X
X if (length <= 0 || CheckRequest(ioreq, unit))
X goto end;
X
X GetTrack(ioreq, side, cylinder);
X for (;;) {
X CopyMem(userbuf, unit->mu_TrackBuffer + MS_BPS * sector, (long) MS_BPS);
X unit->mu_TrackChanged = 1;
X unit->mu_SectorStatus[sector] = CRC_CHANGED;
X
X ioreq->iotd_Req.io_Actual += MS_BPS;
X if (--length <= 0)
X break;
X userbuf += MS_BPS;
X /*
X * Get next sequential sector/side/track
X */
X if (++sector >= spt) {
X sector = 0;
X if (++side >= MS_NSIDES) {
X side = 0;
X if (++cylinder >= unit->mu_NumCyls)
X goto end;
X }
X GetTrack(ioreq, side, cylinder);
X }
X }
X
X if (length)
X ioreq->iotd_Req.io_Error = TDERR_NotSpecified;
X
Xend:
X TermIO(ioreq);
X}
X
X/*
X * This is called by your GetSTS() routine if the Track has changed. It
X * writes the changes back to the disk (a whole track at a time). It has
X * to be called if your device gets a CLOSE instruction too.
X */
X
Xvoid
XInternal_Update(ioreq, unit)
Xstruct IOExtTD *ioreq;
Xregister UNIT *unit;
X{
X debug(("Internal_Update "));
X /* did we have a changed sector at all */
X if (unit->mu_TrackChanged != 0) {
X debug(("needs to write "));
X
X if (unit->mu_SectorsPerTrack > unit->mu_CurrentSectors)
X unit->mu_CurrentSectors = unit->mu_SectorsPerTrack;
X
X /*
X * Only recalculate the CRC on changed sectors. This way, a
X * sector with a bad CRC won't suddenly be ``repaired''.
X */
X {
X register int i;
X
X for (i = unit->mu_CurrentSectors - 1; i >= 0; i--) {
X if (unit->mu_SectorStatus[i] == CRC_CHANGED) {
X unit->mu_CrcBuffer[i] = DataCRC(unit->mu_TrackBuffer + i * MS_BPS);
X debug(("%d: %04x\n", i, unit->mu_CrcBuffer[i]));
X }
X }
X }
X {
X DEV *dev;
X register struct IOExtTD *tdreq;
X word SectorGap;
X
X dev = (DEV *) ioreq->iotd_Req.io_Device;
X tdreq = unit->mu_DiskIOReq;
X SectorGap = CalculateGapLength(unit->mu_CurrentSectors);
X
X ObtainSemaphore(&dev->md_HardwareUse);
X EncodeTrack(unit->mu_TrackBuffer, dev->md_Rawbuffer,
X unit->mu_CrcBuffer,
X unit->mu_CurrentTrack, unit->mu_CurrentSide,
X SectorGap, unit->mu_CurrentSectors);
X
X TDMotorOn(tdreq);
X if (TDSeek(unit, ioreq, unit->mu_CurrentTrack)) {
X debug(("Seek error\n"));
X ioreq->iotd_Req.io_Error = TDERR_SeekError;
X goto end;
X }
X HardwareIO(dev, unit, DSKWRITE);
X
X ReleaseSemaphore(&dev->md_HardwareUse);
X unit->mu_TrackChanged = 0;
X }
X }
Xend:
X debug(("done\n"));
X}
X
X/*
X * TD_Format writes one or more whole tracks without reading them first.
X */
X
Xvoid
XTD_Format(ioreq, unit)
Xregister struct IOExtTD *ioreq;
XUNIT *unit;
X{
X register struct IOExtTD *tdreq = unit->mu_DiskIOReq;
X DEV *dev;
X short side;
X int cylinder;
X byte *userbuf;
X int length;
X word spt;
X word gaplen;
X
X debug(("CMD_Format "));
X
X if (CheckRequest(ioreq, unit))
X goto end;
X
X userbuf = (byte *) ioreq->iotd_Req.io_Data;
X length = ioreq->iotd_Req.io_Length / MS_BPS; /* Sector count */
X cylinder = ioreq->iotd_Req.io_Offset / MS_BPS; /* Sector number */
X /*
X * Now try to guess the number of sectors the user wants per track.
X * 40 sectors is the first ambuiguous length.
X */
X if (length < 40) {
X if (length > 0) {
X for (spt = 8; spt <= MS_SPT_MAX; spt++) {
X if ((length % spt) == 0)
X goto found_spt;
X }
X }
X /*
X * Not 8, 16, 24, 32, 9, 18, 27, 36, 10, 20, or 30? That is an error.
X */
X ioreq->iotd_Req.io_Error = IOERR_BADLENGTH;
X goto end;
X } else /* assume previous number */
X spt = unit->mu_SectorsPerTrack;
X
Xfound_spt:
X gaplen = CalculateGapLength(spt);
X
X /*
X * Assume the whole disk will have this layout.
X */
X unit->mu_SectorsPerTrack = spt;
X
X length /= spt;
X cylinder /= spt;
X
X side = cylinder % MS_NSIDES;
X cylinder /= MS_NSIDES;
X debug(("userbuf %08lx cylinder %d len %d\n", userbuf, cylinder, length));
X
X ioreq->iotd_Req.io_Actual = 0;
X
X /*
X * Write out the current track if we are not going to overwrite it.
X * After the format operation, the buffer is invalidated.
X */
X if (cylinder <= unit->mu_CurrentTrack &&
X unit->mu_CurrentTrack < cylinder + length)
X Internal_Update(ioreq, unit);
X
X dev = (DEV *) ioreq->iotd_Req.io_Device;
X
X while (length > 0) {
X {
X register int i;
X
X for (i = spt - 1; i >= 0; i--) {
X unit->mu_CrcBuffer[i] = DataCRC(userbuf + i * MS_BPS);
X debug(("%d: %04x\n", i, unit->mu_CrcBuffer[i]));
X }
X }
X ObtainSemaphore(&dev->md_HardwareUse);
X EncodeTrack(userbuf, dev->md_Rawbuffer, unit->mu_CrcBuffer,
X cylinder, side,
X gaplen, spt);
X
X TDMotorOn(tdreq);
X if (TDSeek(unit, ioreq, cylinder)) {
X debug(("Seek error\n"));
X ioreq->iotd_Req.io_Error = IOERR_BADLENGTH;
X break;
X }
X unit->mu_CurrentSide = side;
X HardwareIO(dev, unit, DSKWRITE);
X
X ReleaseSemaphore(&dev->md_HardwareUse);
X
X length--;
X userbuf += MS_BPS * spt;
X ioreq->iotd_Req.io_Actual += MS_BPS * spt;
X
X if (++side >= MS_NSIDES) {
X side = 0;
X if (++cylinder >= unit->mu_NumCyls)
X goto end;
X }
X }
Xend:
X unit->mu_CurrentSide = -1;
X TermIO(ioreq);
X}
X/* INDENT OFF */
X
X#asm
X
X; we need a buffer for the Sector-ID field to calculate its checksum
X;SectorHeader:
X; dc.b 0 ; track
X; dc.b 0 ; side
X; dc.b 0 ; sector
X; dc.b 2 ; length (2=512 bytes)
X; dc.w 0 ; CRC
X
X public _EncodeTrack
X
X; EncodeTrack(TrackBuffer, Rawbuffer, Crcs, Track, Side, GapLen, NumSecs)
X; 4 4 4 2 2 2 2
X
X_EncodeTrack:
X movem.l d2-d7/a2-a6,-(sp) ; save registers
X
Xfp set (4*(6+5))+4 ; 4 for return address
Xtrackbf set 0
Xrawbf set 4
Xcrcs set 8
Xtrack set 12
Xside set 14
Xgaplen set 16
Xnumsecs set 18
X
X; a0 ptr in encoded data (also putmfmbyte)
X; a2 ptr to mfm encoding table (putmfmbyte)
X; a3 ptr to data to be crc'd (HeaderCRC)
X; a4 ptr to table with calculated CRC's
X; a5 ptr to unencoded data
X
X; d0 byte to be encoded (putmfmbyte)
X; d1 trashed by putmfmbyte
X; d3 used by putmfmbyte
X; d5 sector number
X; d6 general counter of byte spans
X; d7 sector countdown
X
X sub.w #2,fp+gaplen(sp) ; gap length between sectors
X move.l fp+rawbf(sp),a0 ; pointer to mfmencoded buffer
X move.l fp+crcs(sp),a4 ; pointer to precalculated CRCs
X move.l fp+trackbf(sp),a5 ; pointer to unencoded data
X lea _MfmEncode,a2 ; pointer to MFM lookup table
X
X move.w #$9254,d0 ; a track starts with a gap
X moveq #INDEXGAP-1,d6 ; (60 * $4e)
Xingl move.w d0,(a0)+ ; mfmencoded = $9254
X dbf d6,ingl
X
X lea -6(sp),sp ; Reserve room for SectorHeader
Xfp set fp+6
X move.w fp+numsecs(sp),d7 ; number of sectors to encode
X subq.w #1,d7 ; minus 1 for dbra
X moveq #0,d5 ; start with first sector
X
Xsecloop:
X move.w #$aaaa,d0 ; a sector starts with a gap containing
X moveq #IDGAP2-1,d6 ; 12 * 0 (mfm = $aaaa)
Xid2gl move.w d0,(a0)+
X dbf d6,id2gl
X
X move.w #SYNC,d0 ; The ID field begins here, starting
X move.w d0,(a0)+ ; with 3 syncs (3 * $a1) with a missing
X move.w d0,(a0)+ ; clock bit
X move.w d0,(a0)+
X
X move.w #$5554,(a0)+ ; ID-Address mark ($fe)
X
X move.l sp,a3 ; pointer to Sector-ID buffer
X
X moveq #$5554&1,d3 ; preload d3 for the putmfmbyte routine
X move.b fp+track+1(sp),0(a3) ; insert current track number
X move.b fp+side+1(sp),1(a3) ; side number
X addq.w #1,d5 ; sectors start with 1 instead of 0
X move.b d5,2(a3) ; sector number
X move.b #MS_BPScode,3(a3) ; sector length 512 bytes
X bsr HeaderCRC ; calculate checksum
X move.w d0,IDDATA(a3) ; put it past the data
X
X moveq #IDDATA+IDCRC-1,d6 ; 6 bytes Sector-ID
Xsidl move.b (a3)+,d0 ; get one byte
X bsr putmfmbyte ; encode it
X dbf d6,sidl ; end of buffer ?
X
X moveq #$4e,d0 ; recalculate the MFM value of the
X bsr putmfmbyte ; first gap byte
X
X moveq #DATAGAP1-1-1,d6 ; GAP consisting of
X move.w #$9254,d0 ; 22 * $4e
Xdg1l move.w d0,(a0)+
X dbf d6,dg1l
X
X moveq #DATAGAP2-1,d6 ; GAP consisting of
X move.w #$aaaa,d0 ; 12 * 0 (mfm = $aaaa)
Xdg2l move.w d0,(a0)+
X dbf d6,dg2l
X
X move.w #SYNC,d0 ; Sector data
X move.w d0,(a0)+ ; starts with 3 syncs
X move.w d0,(a0)+
X move.w d0,(a0)+
X
X move.w #$5545,(a0)+ ; Data Address Mark ($fb)
X
X moveq #$5545&1,d3 ; preload d3
X move #MS_BPS-1,d6 ; a sector has 512 bytes
Xdblockl move.b (a5)+,d0 ; get one byte from the buffer
X bsr putmfmbyte ; encode it
X dbf d6,dblockl ; end of sector ?
X
X move.b (a4)+,d0 ; get first byte of CRC
X bsr putmfmbyte ; encode it
X move.b (a4)+,d0 ; get second byte
X bsr putmfmbyte ; encode it
X
X moveq #$4e,d0 ; recalculate the MFM value of the
X bsr putmfmbyte ; first gap byte -> -1 in following loop
X
X; moveq #DATAGAP3-1-1,d6 ; sector ends with a gap
X move.w fp+gaplen(sp),d6 ; sector ends with a gap, -1 for dbf
X move.w #$9254,d0 ; 80 * $4e
Xdg3l move.w d0,(a0)+
X dbf d6,dg3l
X
X dbf d7,secloop ; next sector. d5 has been incremented
X
X lea 6(sp),sp ; Free room for SectorHeader
Xfp set fp-6
X
X move.l fp+rawbf(sp),d6 ; pointer to mfmencoded buffer
X add.l #WLEN,d6 ; end of encoded buffer
X move.l a0,d0 ; (I really want to sub.l a0,d6 )
X sub.l d0,d6 ; length of the remains
X lsr.l #1,d6 ; turn into words
X
X move.w #$9254,d0 ; Fill the end of the track with $4e
Xendgl move.w d0,(a0)+ ; $9254 mfm encoded
X dbf d6,endgl
X
X movem.l (sp)+,d2-d7/a2-a6
X rts
X
X; putmfmbyte encodes one byte (in D0) into MSDOS MFM format to the location
X; pointed by A0. D3 has to be preserved between calls !
X; A2 must contain the pointer to the encoding table.
X; Destroys D0, D1. Updates A0 and D3. Requires A0, D0, D3.
X
Xputmfmbyte
X moveq #16-4,d1
X lsl.l d1,d0 ; split the byte into two nibbles
X lsr.w d1,d0 ; low nibble is in bits 0..15
X ; high nibble in bits 16..31
X swap d0 ; process high nibble first
X and.w #$0f,d0 ; mask out unwanted bits
X move.b 0(a2,d0.w),d1 ; get mfmencoded nibble from table
X btst #6,d1 ; we now have to work out if
X bne.s 1$ ; the high bit of the unencoded data
X btst #0,d3 ; byte and the low bit of the last
X bne.s 1$ ; encoded data are both 0. if this is the
X bset #7,d1 ; case the first clock bit has to be '1'
X1$ move.b d1,(a0)+ ; write high (encoded) nibble
X swap d0 ; process low nibble
X move.b 0(a2,d0.w),d3 ; ....same as above
X btst #6,d3
X bne.s 2$
X btst #0,d1
X bne.s 2$
X bset #7,d3
X2$ move.b d3,(a0)+
X rts
X
X#endasm
X#endif /* READONLY */
X#asm
X
X; The CRC is computed not only over the actual data, but including
X; the SYNC mark (3 * $a1) and the 'ID/DATA - Address Mark' ($fe/$fb).
X; As we don't read or encode these fields into our buffers, we have to
X; preload the registers containing the CRC with the values they would have
X; after stepping over these fields.
X;
X; How CRCs "really" work:
X;
X; First, you should regard a bitstring as a series of coefficients of
X; polymomials. We calculate with these polynomials in modulo-2
X; arithmetic, in which both add and subtract are done the same as
X; exclusive-or. Now, we modify our data (a very long polynomial) in
X; such a way that it becomes divisible by the CCITT-standard 16-bit
X; 16 12 5
X; polynomial: x + x + x + 1, represented by $11021. The easiest
X; way to do this would be to multiply (using proper arithmetic) our
X; datablock with $11021. So we have:
X; data * $11021 =
X; data * ($10000 + $1021) =
X; data * $10000 + data * $1021
X; The left part of this is simple: Just add two 0 bytes. But then
X; the right part (data $1021) remains difficult and even could have
X; a carry into the left part. The solution is to use a modified
X; multiplication, which has a result that is not correct, but with
X; a difference of any multiple of $11021. We then only need to keep
X; the 16 least significant bits of the result.
X;
X; The following algorithm does this for us:
X;
X; unsigned char *data, c, crclo, crchi;
X; while (not done) {
X; c = *data++ + crchi;
X; crchi = (@ c) >> 8 + crclo;
X; crclo = @ c;
X; }
X;
X; Remember, + is done with EOR, the @ operator is in two tables (high
X; and low byte separately), which is calculated as
X;
X; $1021 * (c & $F0)
X; xor $1021 * (c & $0F)
X; xor $1021 * (c >> 4) (* is regular multiplication)
X;
X;
X; Anyway, the end result is the same as the remainder of the division of
X; the data by $11021. I am afraid I need to study theory a bit more...
X
X
X; This is the entry to calculate the checksum for the sector-id field
X; requires: a3 = pointer to the unencoded data
X; returns: d0 = CRC
X
XHeaderCRC:
X movem.l d1-d3/a3-a5,-(sp) ; save registers
X move.w #$b2,d0 ; preload registers
X moveq #$30,d1 ; (CRC for $a1,$a1,$a1,$fb)
X moveq #3,d3 ; calculate checksum for 4 bytes
X bra.s getCRC ; (=track,side,sector,sectorlength)
X
X; This is the entry to calculate the checksum for the data field
X; requires: a3 = pointer to the unencoded data
X; returns: d0 = CRC
X
XDataCRC:
X movem.l d1-d3/a3-a5,-(sp) ; save registers
X bra.s DataCRC1
X
X; C entry point for DataCRC(byte *data)
X
X_DataCRC:
X movem.l d1-d3/a3-a5,-(sp) ; save registers
Xfp set (4*(3+3))+4
Xdata set 0
X move.l fp+data(sp),a3 ; get parameter
XDataCRC1:
X move.w #$e2,d0 ; preload the CRC registers
X move.w #$95,d1 ; (CRC for $a1,$a1,$a1,$fe)
X move.w #MS_BPS-1,d3 ; a sector 512 bytes
X
XgetCRC lea CRCTable1,a4
X lea CRCTable2,a5
X moveq #0,d2
X
X1$ move.b (a3)+,d2
X eor.b d0,d2
X move.b 0(a4,d2.w),d0
X eor.b d1,d0
X move.b 0(a5,d2.w),d1
X dbf d3,1$
X
X lsl.w #8,d0
X move.b d1,d0
X movem.l (sp)+,d1-d3/a3-a5
X rts
X
X
XCRCTable1:
X dc.b $00,$10,$20,$30,$40,$50,$60,$70,$81,$91,$a1,$b1,$c1,$d1,$e1,$f1
X dc.b $12,$02,$32,$22,$52,$42,$72,$62,$93,$83,$b3,$a3,$d3,$c3,$f3,$e3
X dc.b $24,$34,$04,$14,$64,$74,$44,$54,$a5,$b5,$85,$95,$e5,$f5,$c5,$d5
X dc.b $36,$26,$16,$06,$76,$66,$56,$46,$b7,$a7,$97,$87,$f7,$e7,$d7,$c7
X dc.b $48,$58,$68,$78,$08,$18,$28,$38,$c9,$d9,$e9,$f9,$89,$99,$a9,$b9
X dc.b $5a,$4a,$7a,$6a,$1a,$0a,$3a,$2a,$db,$cb,$fb,$eb,$9b,$8b,$bb,$ab
X dc.b $6c,$7c,$4c,$5c,$2c,$3c,$0c,$1c,$ed,$fd,$cd,$dd,$ad,$bd,$8d,$9d
X dc.b $7e,$6e,$5e,$4e,$3e,$2e,$1e,$0e,$ff,$ef,$df,$cf,$bf,$af,$9f,$8f
X dc.b $91,$81,$b1,$a1,$d1,$c1,$f1,$e1,$10,$00,$30,$20,$50,$40,$70,$60
X dc.b $83,$93,$a3,$b3,$c3,$d3,$e3,$f3,$02,$12,$22,$32,$42,$52,$62,$72
X dc.b $b5,$a5,$95,$85,$f5,$e5,$d5,$c5,$34,$24,$14,$04,$74,$64,$54,$44
X dc.b $a7,$b7,$87,$97,$e7,$f7,$c7,$d7,$26,$36,$06,$16,$66,$76,$46,$56
X dc.b $d9,$c9,$f9,$e9,$99,$89,$b9,$a9,$58,$48,$78,$68,$18,$08,$38,$28
X dc.b $cb,$db,$eb,$fb,$8b,$9b,$ab,$bb,$4a,$5a,$6a,$7a,$0a,$1a,$2a,$3a
X dc.b $fd,$ed,$dd,$cd,$bd,$ad,$9d,$8d,$7c,$6c,$5c,$4c,$3c,$2c,$1c,$0c
X dc.b $ef,$ff,$cf,$df,$af,$bf,$8f,$9f,$6e,$7e,$4e,$5e,$2e,$3e,$0e,$1e
X
XCRCTable2:
X dc.b $00,$21,$42,$63,$84,$a5,$c6,$e7,$08,$29,$4a,$6b,$8c,$ad,$ce,$ef
X dc.b $31,$10,$73,$52,$b5,$94,$f7,$d6,$39,$18,$7b,$5a,$bd,$9c,$ff,$de
X dc.b $62,$43,$20,$01,$e6,$c7,$a4,$85,$6a,$4b,$28,$09,$ee,$cf,$ac,$8d
X dc.b $53,$72,$11,$30,$d7,$f6,$95,$b4,$5b,$7a,$19,$38,$df,$fe,$9d,$bc
X dc.b $c4,$e5,$86,$a7,$40,$61,$02,$23,$cc,$ed,$8e,$af,$48,$69,$0a,$2b
X dc.b $f5,$d4,$b7,$96,$71,$50,$33,$12,$fd,$dc,$bf,$9e,$79,$58,$3b,$1a
X dc.b $a6,$87,$e4,$c5,$22,$03,$60,$41,$ae,$8f,$ec,$cd,$2a,$0b,$68,$49
X dc.b $97,$b6,$d5,$f4,$13,$32,$51,$70,$9f,$be,$dd,$fc,$1b,$3a,$59,$78
X dc.b $88,$a9,$ca,$eb,$0c,$2d,$4e,$6f,$80,$a1,$c2,$e3,$04,$25,$46,$67
X dc.b $b9,$98,$fb,$da,$3d,$1c,$7f,$5e,$b1,$90,$f3,$d2,$35,$14,$77,$56
X dc.b $ea,$cb,$a8,$89,$6e,$4f,$2c,$0d,$e2,$c3,$a0,$81,$66,$47,$24,$05
X dc.b $db,$fa,$99,$b8,$5f,$7e,$1d,$3c,$d3,$f2,$91,$b0,$57,$76,$15,$34
X dc.b $4c,$6d,$0e,$2f,$c8,$e9,$8a,$ab,$44,$65,$06,$27,$c0,$e1,$82,$a3
X dc.b $7d,$5c,$3f,$1e,$f9,$d8,$bb,$9a,$75,$54,$37,$16,$f1,$d0,$b3,$92
X dc.b $2e,$0f,$6c,$4d,$aa,$8b,$e8,$c9,$26,$07,$64,$45,$a2,$83,$e0,$c1
X dc.b $1f,$3e,$5d,$7c,$9b,$ba,$d9,$f8,$17,$36,$55,$74,$93,$b2,$d1,$f0
X
X#endasm
X
X/* INDENT ON */
END_OF_FILE
if test 47667 -ne `wc -c <'src/devio.c'`; then
echo shar: \"'src/devio.c'\" unpacked with wrong size!
fi
# end of 'src/devio.c'
fi
echo shar: End of archive 6 \(of 6\).
cp /dev/null ark6isdone
MISSING=""
for I in 1 2 3 4 5 6 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 6 archives.
rm -f ark[1-9]isdone
else
echo You still need to unpack the following archives:
echo " " ${MISSING}
fi
## End of shell archive.
exit 0
--
Mail submissions (sources or binaries) to <amiga@cs.odu.edu>.
Mail comments to the moderator at <amiga-request@cs.odu.edu>.
Post requests for sources, and general discussion to comp.sys.amiga.